
package Participants.CapitoGuye;

import java.util.List;

import Othello.Move;

public class TreeNode
	{

	/*------------------------------------------------------------------*\
	|*							Constructeurs							*|
	\*------------------------------------------------------------------*/

	public TreeNode(World world, int nbCasesRetournes, Case caseJoue)
		{
		this.world = world;
		this.nbCasesRetournes = nbCasesRetournes;
		this.caseJoue = caseJoue;
		this.plateau = world.getPlateau();
		this.eval = 0;
		}

	public TreeNode(World world)
		{
		this(world, 0, null);
		}

	/*------------------------------------------------------------------*\
	|*							Methodes Public							*|
	\*------------------------------------------------------------------*/

	public List<Case> getCoupJouable()
		{
		return world.getCoupsJouable();
		}

	public TreeNode appliqueCoupVirtuel(Case coupJouable, World world)
		{
		int nbCasesRetournes = world.updateWorld(new Move(coupJouable.getI(), coupJouable.getJ()));
		return new TreeNode(world, nbCasesRetournes, coupJouable);
		}

	/**
	 * Fonction d'evaluation
	 */
	public int situationDuNoeud()
		{
		evaluerForce();

		evaluerCasesFixes();

		//Différence du nombre de pion[-64;64]
		diffPions = world.critereNbPions();

		evaluerMobilite();

		evaluerFrontiere();

		prendreCasesCentrales();

		evaluerLesCoinsAuCarre();

		evaluerLesCoins();

		evaluerLesBords();

		betonnerLesBords();

		//Bonus betonnage [0;3600]
		if (world.DEF_CASE[caseJoue.getI()][caseJoue.getJ()] == Joueur.JOUEUR_ACTUEL)
			{
			bonusBetonnage = 200 * nbCasesRetournes;
			}

		ponderationFinale();

		return eval;
		}

	/*------------------------------*\
	|*				Get				*|
	\*------------------------------*/

	public World getWorld()
		{
		return world;

		}

	/*------------------------------*\
	|*			 Object				*|
	\*------------------------------*/

	public boolean isFinal()
		{
		boolean isFinal = false;

		if (world.getCoupsJouable().isEmpty())
			{
			Joueur.JOUEUR_ACTUEL = Joueur.inverseJoueur();
			if (world.getCoupsJouable().isEmpty())
				{
				isFinal = true;

				}
			Joueur.JOUEUR_ACTUEL = Joueur.inverseJoueur();

			}

		return isFinal;

		}

	/*------------------------------------------------------------------*\
	|*							Methodes Private						*|
	\*------------------------------------------------------------------*/

	private void ponderationFinale()
		{
		if (world.getEtatPartie() == EtatPartie.ouverture)
			{
			eval += 4 * bonusCentral + diffMobilite;
			}
		else if (world.getEtatPartie() == EtatPartie.debut)
			{
			eval += 20 * diffMobilite + 10 * bonusMobilite + 10 * diffFrontiere;
			}
		else if (world.getEtatPartie() == EtatPartie.millieu)
			{
			eval += diffForce + bonusCasesDefinitives + bonusBetonnage + diffCasesDefinitives + 5 * diffAdjacent + 10 * diffBord + 10 * diffCoin + 100 * bonusCoin + 10 * diffMobilite + 20 * diffFrontiere + 20 * bonusMobilite + 40 * diffPions + 120 * betonnage;
			}
		else
			{
			eval += 100 * diffPions + bonusCasesDefinitives;
			}
		}

	private void evaluerLesCoins()
		{
		//Eval bonus des coins et cases adjacentes [-51;51]
		bonusCoin += evaluerUnCoin(0, 0, 1, 1);
		bonusCoin += evaluerUnCoin(7, 7, -1, -1);
		bonusCoin += evaluerUnCoin(0, 7, 1, -1);
		bonusCoin += evaluerUnCoin(7, 0, -1, 1);
		}

	private int evaluerUnCoin(int iCoin, int jCoin, int iVecteur, int jVecteur)
		{
		Case[][] plateau = world.getPlateau();
		if (caseJoue.getI() == iCoin && caseJoue.getJ() == jCoin)
			{
			return 50;
			}
		else if (plateau[iCoin][jCoin].getVal() == Joueur.JOUEUR_ACTUEL)
			{
			return 20 + evaluerAdjacentsAuCoin(iCoin, jCoin, iVecteur, jVecteur, -1);
			}
		else if (plateau[iCoin][jCoin].getVal() == Joueur.inverseJoueur())
			{
			return -20 + evaluerAdjacentsAuCoin(iCoin, jCoin, iVecteur, jVecteur, 1);
			}
		else
			{
			if (Joueur.JOUEUR_ACTUEL == Joueur.PLAYERID)
				{
				return evaluerAdjacentsAuCoin(iCoin, jCoin, iVecteur, jVecteur, 1);
				}
			else
				{
				return evaluerAdjacentsAuCoin(iCoin, jCoin, iVecteur, jVecteur, -1);
				}
			}
		}

	private int evaluerAdjacentsAuCoin(int iCoin, int jCoin, int iVecteur, int jVecteur, int signe)
		{
		Case[][] plateau = world.getPlateau();
		if (plateau[iCoin + iVecteur][jCoin].getVal() == Joueur.JOUEUR_ACTUEL)
			{
			return signe * 12;
			}
		else if (plateau[iCoin + iVecteur][jCoin].getVal() == Joueur.inverseJoueur())
			{
			return signe * 12;
			}

		if (plateau[iCoin][jCoin + jVecteur].getVal() == Joueur.JOUEUR_ACTUEL)
			{
			return signe * 12;
			}
		else if (plateau[iCoin][jCoin + jVecteur].getVal() == Joueur.inverseJoueur())
			{
			return signe * 12;
			}

		if (plateau[iCoin + iVecteur][jCoin + jVecteur].getVal() == Joueur.JOUEUR_ACTUEL)
			{
			return signe * 7;
			}
		else if (plateau[iCoin + iVecteur][jCoin + jVecteur].getVal() == Joueur.inverseJoueur())
			{
			return signe * 7;
			}
		return 0;
		}

	private boolean isPionStableEnI(int i, int j, int j2)
		{
		for(int k = j; k > j2; k--)
			{
			if (world.getPlateau()[i][k].getVal() == Joueur.inverseJoueur()) { return false; }
			}
		return true;
		}

	private boolean isPionStableEnJ(int i, int j, int i2)
		{
		for(int k = i; k > i2; k--)
			{
			if (world.getPlateau()[k][j].getVal() == Joueur.inverseJoueur()) { return false; }
			}
		return true;
		}

	private void evaluerForce()
		{
		//Difference de force selon le tableau [-6632;6632]
		int f[] = world.forceOccupation();
		diffForce = f[Joueur.JOUEUR_ACTUEL] - f[Joueur.inverseJoueur()];
		}

	private void betonnerLesBords()
		{
		// Betonner selon les coins
		if (caseJoue.isInAnEdge())
			{
			if (caseJoue.getI() == 0) // bord gauche
				{
				if (isPionStableEnI(0, 7, caseJoue.getJ()) || isPionStableEnI(0, caseJoue.getJ(), 0))
					{
					betonnage += 5;
					}
				}
			else if (caseJoue.getI() == 7) // bord droite
				{
				if (isPionStableEnI(7, 7, caseJoue.getJ()) || isPionStableEnI(7, caseJoue.getJ(), 0))
					{
					betonnage += 5;
					}
				}
			else if (caseJoue.getJ() == 0) // bord haut
				{
				if (isPionStableEnJ(7, 0, caseJoue.getI()) || isPionStableEnJ(caseJoue.getI(), 0, 0))
					{
					betonnage += 5;
					}
				}
			else
				// bord bas
				{
				if (isPionStableEnJ(7, 7, caseJoue.getI()) || isPionStableEnJ(caseJoue.getI(), 7, 0))
					{
					betonnage += 5;
					}
				}
			}
		}

	private void evaluerLesBords()
		{
		//Difference de Bords sans cases adjacentes et coins [-16;16]
		for(int j = 2; j < 6; j++)
			{
			if (plateau[0][j].getVal() == Joueur.JOUEUR_ACTUEL)
				{
				diffBord += 10;
				}
			else if (plateau[0][j].getVal() == Joueur.inverseJoueur())
				{
				diffBord -= 10;
				}
			if (plateau[7][j].getVal() == Joueur.JOUEUR_ACTUEL)
				{
				diffBord += 10;
				}
			else if (plateau[7][j].getVal() == Joueur.inverseJoueur())
				{
				diffBord -= 10;
				}
			}
		for(int i = 2; i < 6; i++)
			{
			if (plateau[i][0].getVal() == Joueur.JOUEUR_ACTUEL)
				{
				diffBord += 10;
				}
			else if (plateau[i][0].getVal() == Joueur.inverseJoueur())
				{
				diffBord -= 10;
				}
			if (plateau[i][7].getVal() == Joueur.JOUEUR_ACTUEL)
				{
				diffBord += 10;
				}
			else if (plateau[i][7].getVal() == Joueur.inverseJoueur())
				{
				diffBord -= 10;
				}
			}
		}

	private void evaluerLesCoinsAuCarre()
		{
		//Difference de coin au carré [-16;16]
		//Diffenrece de case adjacente à un coin si pas posséder [-12;12]
		if (plateau[0][0].getVal() == Joueur.JOUEUR_ACTUEL)
			{
			diffCoin += 1;
			}
		else if (plateau[0][0].getVal() == Joueur.inverseJoueur())
			{
			diffCoin -= 1;
			}
		else
			{
			if (plateau[1][0].getVal() == Joueur.JOUEUR_ACTUEL)
				{
				diffAdjacent -= 1;
				}
			else if (plateau[1][0].getVal() == Joueur.inverseJoueur())
				{
				diffAdjacent += 1;
				}

			if (plateau[0][1].getVal() == Joueur.JOUEUR_ACTUEL)
				{
				diffAdjacent -= 1;
				}
			else if (plateau[0][1].getVal() == Joueur.inverseJoueur())
				{
				diffAdjacent += 1;
				}

			if (plateau[1][1].getVal() == Joueur.JOUEUR_ACTUEL)
				{
				diffAdjacent -= 1;
				}
			else if (plateau[1][1].getVal() == Joueur.inverseJoueur())
				{
				diffAdjacent += 1;
				}
			}

		if (plateau[0][7].getVal() == Joueur.JOUEUR_ACTUEL)
			{
			diffCoin += 1;
			}
		else if (plateau[0][7].getVal() == Joueur.inverseJoueur())
			{
			diffCoin -= 1;
			}
		else
			{
			if (plateau[0][6].getVal() == Joueur.JOUEUR_ACTUEL)
				{
				diffAdjacent -= 1;
				}
			else if (plateau[0][6].getVal() == Joueur.inverseJoueur())
				{
				diffAdjacent += 1;
				}
			if (plateau[1][7].getVal() == Joueur.JOUEUR_ACTUEL)
				{
				diffAdjacent -= 1;
				}
			else if (plateau[1][7].getVal() == Joueur.inverseJoueur())
				{
				diffAdjacent += 1;
				}
			if (plateau[1][6].getVal() == Joueur.JOUEUR_ACTUEL)
				{
				diffAdjacent -= 1;
				}
			else if (plateau[1][6].getVal() == Joueur.inverseJoueur())
				{
				diffAdjacent += 1;
				}
			}

		if (plateau[7][0].getVal() == Joueur.JOUEUR_ACTUEL)
			{
			diffCoin += 1;
			}
		else if (plateau[7][0].getVal() == Joueur.inverseJoueur())
			{
			diffCoin -= 1;
			}
		else
			{
			if (plateau[6][0].getVal() == Joueur.JOUEUR_ACTUEL)
				{
				diffAdjacent -= 1;
				}
			else if (plateau[6][0].getVal() == Joueur.inverseJoueur())
				{
				diffAdjacent += 1;
				}
			if (plateau[7][1].getVal() == Joueur.JOUEUR_ACTUEL)
				{
				diffAdjacent -= 1;
				}
			else if (plateau[7][1].getVal() == Joueur.inverseJoueur())
				{
				diffAdjacent += 1;
				}
			if (plateau[6][1].getVal() == Joueur.JOUEUR_ACTUEL)
				{
				diffAdjacent -= 1;
				}
			else if (plateau[6][1].getVal() == Joueur.inverseJoueur())
				{
				diffAdjacent += 1;
				}
			}

		if (plateau[7][7].getVal() == Joueur.JOUEUR_ACTUEL)
			{
			diffCoin += 1;
			}
		else if (plateau[7][7].getVal() == Joueur.inverseJoueur())
			{
			diffCoin -= 1;
			}
		else
			{
			if (plateau[7][6].getVal() == Joueur.JOUEUR_ACTUEL)
				{
				diffAdjacent -= 1;
				}
			else if (plateau[7][6].getVal() == Joueur.inverseJoueur())
				{
				diffAdjacent += 1;
				}
			if (plateau[6][7].getVal() == Joueur.JOUEUR_ACTUEL)
				{
				diffAdjacent -= 1;
				}
			else if (plateau[6][7].getVal() == Joueur.inverseJoueur())
				{
				diffAdjacent += 1;
				}
			if (plateau[6][6].getVal() == Joueur.JOUEUR_ACTUEL)
				{
				diffAdjacent -= 1;
				}
			else if (plateau[6][6].getVal() == Joueur.inverseJoueur())
				{
				diffAdjacent += 1;
				}
			}

		if (diffCoin > 0)
			{
			diffCoin *= diffCoin;
			}
		else
			{
			diffCoin *= -diffCoin;
			}

		if (diffAdjacent > 0)
			{
			diffAdjacent *= diffAdjacent;
			}
		else
			{
			diffAdjacent *= -diffAdjacent;
			}
		}

	private void prendreCasesCentrales()
		{
		//Prendre les cases centrales dans les premiers coups [-60;60]
		for(int i = 3; i < 5; i++)
			{
			for(int j = 3; j < 5; j++)
				{
				if (plateau[i][j].getVal() == Joueur.JOUEUR_ACTUEL)
					{
					bonusCentral += 15;
					}
				else
					{
					bonusCentral -= 15;
					}
				}
			}
		}

	private void evaluerFrontiere()
		{
		//Frontiere [-32;32]
		for(int i = 0; i < 8; i++)
			{
			for(int j = 0; j < 8; j++)
				{
				if (plateau[i][j].getVal() == Joueur.JOUEUR_ACTUEL)
					{
					diffFrontiere -= world.hasCaseVide(plateau[i][j]) ? 1 : 0;
					}
				else if (plateau[i][j].getVal() == Joueur.inverseJoueur())
					{
					diffFrontiere += world.hasCaseVide(plateau[i][j]) ? 1 : 0;
					}
				}
			}
		}

	private void evaluerMobilite()
		{
		//Difference de coup possible[-32;32]
		int mob[] = world.forceMobilite();
		diffMobilite = mob[Joueur.JOUEUR_ACTUEL] - mob[Joueur.inverseJoueur()];

		//Bonus de coup possible[-50;50]
		if (Joueur.JOUEUR_ACTUEL == Joueur.PLAYERID)
			{
			if (mob[Joueur.inverseJoueur()] < 1)
				{
				bonusMobilite += 30;
				}
			else if (mob[Joueur.JOUEUR_ACTUEL] < 2)
				{
				bonusMobilite += 20;
				}
			else if (mob[Joueur.JOUEUR_ACTUEL] < 3)
				{
				bonusMobilite += 10;
				}
			else if (mob[Joueur.JOUEUR_ACTUEL] < 4)
				{
				bonusMobilite += 5;
				}
			else
				{
				bonusMobilite -= 50;
				}
			}
		else
			{
			if (mob[Joueur.inverseJoueur()] < 1)
				{
				bonusMobilite -= 30;
				}
			else if (mob[Joueur.JOUEUR_ACTUEL] < 2)
				{
				bonusMobilite -= 20;
				}
			else if (mob[Joueur.JOUEUR_ACTUEL] < 3)
				{
				bonusMobilite -= 10;
				}
			else if (mob[Joueur.JOUEUR_ACTUEL] < 4)
				{
				bonusMobilite -= 5;
				}
			else
				{
				bonusMobilite += 50;
				}
			}
		}

	private void evaluerCasesFixes()
		{
		//Diffenrence de case fixe [-64;64]
		int d[] = world.forceDef();
		diffCasesDefinitives = d[Joueur.JOUEUR_ACTUEL] - d[Joueur.inverseJoueur()];

		//bonus de case fixe[-5000;5000]
		int casePourGagner = 33 - d[Joueur.JOUEUR_ACTUEL];
		int casePourPerdre = 33 - d[Joueur.inverseJoueur()];
		if (casePourGagner < 0)
			{
			bonusCasesDefinitives += 5000;
			}
		else if (casePourGagner < 5)
			{
			bonusCasesDefinitives += 40;
			}
		else if (casePourGagner < 10)
			{
			bonusCasesDefinitives += 30;
			}
		else if (casePourGagner < 15)
			{
			bonusCasesDefinitives += 20;
			}
		else if (casePourGagner < 20)
			{
			bonusCasesDefinitives += 10;
			}
		else if (casePourGagner < 25)
			{
			bonusCasesDefinitives += 5;
			}
		if (casePourPerdre < 0)
			{
			bonusCasesDefinitives -= 5000;
			}
		else if (casePourPerdre < 5)
			{
			bonusCasesDefinitives -= 40;
			}
		else if (casePourPerdre < 10)
			{
			bonusCasesDefinitives -= 30;
			}
		else if (casePourPerdre < 15)
			{
			bonusCasesDefinitives -= 20;
			}
		else if (casePourPerdre < 20)
			{
			bonusCasesDefinitives -= 10;
			}
		else if (casePourPerdre < 25)
			{
			bonusCasesDefinitives -= 5;
			}
		}

	/*------------------------------------------------------------------*\
	|*							Attributs Private						*|
	\*------------------------------------------------------------------*/

	// Input
	private World world;
	private int nbCasesRetournes;
	private Case caseJoue;
	private Case plateau[][];

	// Tools
	private int diffAdjacent = 0; //[-12;12]
	private int diffCoin = 0; //[-16;16]
	private int diffBord = 0; //[-16;16]
	private int diffCasesDefinitives = 0; //[-64;64]
	private int diffForce = 0; //[-6632;6632]
	private int diffFrontiere = 0; //[-32;32]
	private int diffPions = 0; //[-64;64]
	private int diffMobilite = 0; //[-32;32]

	private int betonnage = 0;
	private int bonusBetonnage = 0; //[0;3600]
	private int bonusCentral = 0; //[-60;60]
	private int bonusCoin = 0; //[-51;51]
	private int bonusCasesDefinitives = 0; //[-5000;5000]
	private int bonusMobilite = 0; //[-50;50]

	// Output
	private int eval;
	}
